/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package practica1.ui;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import practica1.actions.*;
import practica1.controller.Controller;
import practica1.controller.adapter.ControllerExperimentoAdapter;
import practica1.controller.adapter.ControllerPoblacionAdapter;
import practica1.controller.event.ControllerExperimentoEvent;
import practica1.controller.event.ControllerListenerEvent;
import practica1.controller.event.ControllerPoblacionEvent;
import practica1.controller.listener.ControllerListener;
import practica1.language.Language;
import practica1.language.LanguageEvent;
import practica1.language.LanguageListener;
import practica1.logic.LogicExperimento;
import practica1.logic.LogicPoblacion;


/**
 * Componente que representa el panel donde se visualizan los experimentos
 * y sus poblaciones en un JTree
 * @author Miguel González - Ceura
 */
public class PanelExperimentos extends JPanel implements LanguageListener {
    private JTree arbolExperimentos;
    private DefaultTreeModel modeloArbol;
    private DefaultMutableTreeNode padreArbol;

    private JPopupMenu popupMenuExperimentos;
    private JPopupMenu popupMenuExperimento;
    private JPopupMenu popupMenuPoblacion;
    private Controller controlador;
    
    /**
     * Constructor por defecto del panel
     */
    public PanelExperimentos() {
        controlador = Controller.getInstance();
        
        controlador.addControllerListener(new EscuchadorControllador());
        controlador.addControllerExperimentoListener(
                new EscuchadorExperimentos());
        controlador.addControllerPoblacionListener(
                new EscuchadorPoblaciones());
        
        Language.getI().addLanguageListener(this);
        
        popupMenuExperimentos = new JPopupMenu();
        popupMenuExperimentos.add(new ActionAbrir());
        popupMenuExperimentos.add(new ActionNuevoExperimento());
        popupMenuExperimentos.setOpaque(true);
        
        popupMenuExperimento = new JPopupMenu();
        popupMenuExperimento.add(new ActionNuevaPoblacion());
        popupMenuExperimento.add(new ActionGuardar());
        popupMenuExperimento.add(new ActionGuardarComo());
        popupMenuExperimento.add(new ActionMainExperimento());
        popupMenuExperimento.add(new ActionPropiedades());
        popupMenuExperimento.add(new ActionCerrar());
        popupMenuExperimento.setOpaque(true);
        
        popupMenuPoblacion = new JPopupMenu();
        popupMenuPoblacion.add(new ActionCerrarPoblacion());
        popupMenuPoblacion.add(new ActionBorrarPoblacion());
        popupMenuPoblacion.add(new ActionPropiedadesPoblacion());
        popupMenuPoblacion.setOpaque(true);
        
        
        padreArbol = new DefaultMutableTreeNode(
                Language.getI().getP("EXPERIMENTOS"));
        modeloArbol = new DefaultTreeModel(padreArbol);
        arbolExperimentos = new JTree(modeloArbol);
        arbolExperimentos.setCellRenderer(new TreeCellRendererComponent());
        arbolExperimentos.setShowsRootHandles(true);
        
        arbolExperimentos.getSelectionModel().setSelectionMode
            (TreeSelectionModel.SINGLE_TREE_SELECTION);

        //Listen for when the selection changes.
        arbolExperimentos.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                //Obtenemos el nodo seleccionado
                TreePath ramaNodoSeleccionado = 
                        arbolExperimentos.getSelectionPath();
                if (e.getClickCount() == 2 && e.getButton() == MouseEvent.BUTTON1) {
                    
                    //Comprobamos que tipo de nodo es
                    if(ramaNodoSeleccionado != null &&
                            ramaNodoSeleccionado.getPath().length == 2) {
                        //Es un experimento
                        String nombreExperimento = ramaNodoSeleccionado.toString();
                        
                    } else if(ramaNodoSeleccionado != null &&
                            ramaNodoSeleccionado.getPath().length == 3) {
                        //Es una población
                        String nombrePoblacion = ramaNodoSeleccionado.toString();
                        String nombreExperimento = ramaNodoSeleccionado.
                                getPath()[1].toString();
                        
                        LogicPoblacion poblacion = ((LogicPoblacion)
                                    ((DefaultMutableTreeNode)
                                    ramaNodoSeleccionado.getPath()[2])
                                    .getUserObject());
                        
                        
                        //Abrimos la población
                        controlador.abrirPoblacion(poblacion);
                    }
                } else if(e.getClickCount() == 1 &&
                        e.getButton() == MouseEvent.BUTTON3) {
                    try {
                        //Con el botón derecho tenemos que seleccionar el nodo manualmente
                        int row = arbolExperimentos.getRowForLocation(
                                e.getX(), e.getY());

                        arbolExperimentos.addSelectionRow(row);

                        //Volvemos a pedir el nodo seleccionado
                        ramaNodoSeleccionado = 
                            arbolExperimentos.getSelectionPath();

                        //Comprobamos que tipo de nodo es
                        if(ramaNodoSeleccionado.getPath().length == 1) {
                            //Si es la raíz experimentos
                            popupMenuExperimentos.show((JComponent)e.getSource(), 
                                    e.getX(), e.getY());
                        } else if(ramaNodoSeleccionado.getPath().length == 2) {
                            //Si es un experimento
                            controlador.setExperimentoSeleccionado((LogicExperimento)
                                    ((DefaultMutableTreeNode)
                                    ramaNodoSeleccionado.getPath()[1])
                                    .getUserObject());
                            popupMenuExperimento.show((JComponent)e.getSource(), 
                                    e.getX(), e.getY());
                        } else if(ramaNodoSeleccionado.getPath().length == 3) {
                            //Es una población
                            String nombrePoblacion = ramaNodoSeleccionado.toString();
                            String nombreExperimento = ramaNodoSeleccionado.
                                    getPath()[1].toString();

                            LogicExperimento experimento = ((LogicExperimento)
                                    ((DefaultMutableTreeNode)
                                    ramaNodoSeleccionado.getPath()[1])
                                    .getUserObject());
                            
                            LogicPoblacion poblacion = ((LogicPoblacion)
                                    ((DefaultMutableTreeNode)
                                    ramaNodoSeleccionado.getPath()[2])
                                    .getUserObject());
                            
                            controlador.setExperimentoSeleccionado(experimento);
                            controlador.setPoblacionSeleccionada(poblacion);
                            
                            popupMenuPoblacion.show((JComponent)e.getSource(), 
                                    e.getX(), e.getY());
                        }
                    } catch(Exception ex) {
                        //No hacemos nada, error dado porque no se ha pinchado
                        //en un nodo y falla el getRowForLocation
                    }
                }
            }

            @Override public void mousePressed(MouseEvent e) {}
            @Override public void mouseReleased(MouseEvent e) {}
            @Override public void mouseEntered(MouseEvent e) {}
            @Override public void mouseExited(MouseEvent e) {}
            
        });
        
        initComponents();
    }
   
    /**
     * Inicializa los componentes Swing
     */
    private void initComponents() {
        setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
        setLayout(new BorderLayout());
        
        JScrollPane scrollPane = new JScrollPane(arbolExperimentos);
        
        add(scrollPane, BorderLayout.CENTER);
        
        for(LogicExperimento exp : controlador.getExperimentosAbiertos()) {
            aniadirExperimento(exp);
        }
    }
    
    /**
     * Método que añade los experimentos y sus poblaciones al JTree
     * @param exp LogicExperimento a añadir
     */
    private void aniadirExperimento(LogicExperimento exp) {
        DefaultMutableTreeNode nodoExperimento = new DefaultMutableTreeNode(exp);

        ArrayList<LogicPoblacion> poblaciones = exp.getPoblaciones();

        //Añadimos las poblaciones
        for(LogicPoblacion poblacion : poblaciones) {
            DefaultMutableTreeNode nodoPoblacion = new 
                    DefaultMutableTreeNode(poblacion);
            nodoExperimento.add(nodoPoblacion);
        }

        modeloArbol.insertNodeInto(nodoExperimento, padreArbol, 
                modeloArbol.getChildCount(padreArbol));

        //Mostramos la nueva rama
        arbolExperimentos.scrollPathToVisible(new TreePath(
                nodoExperimento.getPath()));
    }

    /**
     * Método que escucha cuando un experimento es modificado y repinta
     * el JTree por si su nombre ha cambiado
     */
    private class EscuchadorControllador implements ControllerListener {
        @Override
        public void experimentoPrincipalChange(ControllerListenerEvent event) {
            arbolExperimentos.repaint();
        }
    }
    
    /**
     * Clase que escucha los cambios en los experimentos por el controlador
     */
    private class EscuchadorExperimentos extends ControllerExperimentoAdapter {
        /**
         * Método que es llamado cuando se añade un nuevo experimento
         * @param event ControllerExperimentoEvent
         */
        @Override
        public void addedExperimento(ControllerExperimentoEvent event) {
            aniadirExperimento(event.getExperimento());
        }
        
        /**
         * Método que es llamado cuando se quita un experimento
         * @param event ControllerExperimentoEvent
         */
        @Override
        public void removedExperimento(ControllerExperimentoEvent event) {
            boolean removed = false;
            //Recorremos el árbol buscando el experimento a borrar
            for(int i=0; i<arbolExperimentos.getRowCount() && !removed; i++) {
                TreePath pathExp = arbolExperimentos.getPathForRow(i);
                //Si el path es de dos niveles es un experimento
                //Primer nivel es root
                //Segundo nivel el path del experimento
                if(pathExp.getPath().length == 2) {
                    LogicExperimento ex = (LogicExperimento)
                            ((DefaultMutableTreeNode)pathExp.getPath()[1])
                            .getUserObject();

                    if(ex.equals(event.getExperimento())) {
                        padreArbol.remove(i - 1);
                        modeloArbol.reload();
                        removed = true;
                    }
                }
            }
        }
    }
    
    /**
     * Clase que escucha los cambios en las poblaciones
     */
    private class EscuchadorPoblaciones extends ControllerPoblacionAdapter {
        /**
         * Método que es llamado al añadirse una población a un experimento
         * @param event 
         */
        @Override
        public void addedPoblacion(ControllerPoblacionEvent event) {
            LogicPoblacion poblacion = event.getPoblacion();
            
            //Actualizamos el jtree con la nueva población
            DefaultMutableTreeNode nodoPoblacion = new DefaultMutableTreeNode(
                    poblacion);
            
  
            //Buscamos el padre
            boolean enc = false;
            for(int i=0; i<modeloArbol.getChildCount(padreArbol) && !enc; i++) {
                //Obtenemos el modelo de experimento de la rama actual del JTree
                LogicExperimento experimento = ((LogicExperimento)
                        ((DefaultMutableTreeNode)modeloArbol
                        .getChild(padreArbol, i)).getUserObject());
                
                //Si el experimento de la población
                if(experimento.equals(poblacion.getExperimentoPadre())) {
                    //Insertamos la población como hoja del experimento
                    modeloArbol.insertNodeInto
                            (nodoPoblacion, (DefaultMutableTreeNode)modeloArbol
                        .getChild(padreArbol, i), 
                            modeloArbol.getChildCount(
                            (DefaultMutableTreeNode)modeloArbol
                                .getChild(padreArbol, i)));  
                    enc = true;
                }
            }
            
           //Mostramos la nueva rama
            arbolExperimentos.setSelectionPath(new TreePath(
                    nodoPoblacion.getPath()));
            arbolExperimentos.scrollPathToVisible(new TreePath(
                    nodoPoblacion.getPath()));
        }
        
        /**
         * Método que es llamado al quitarse una población de un experimento
         * @param event ControllerPoblacionEvent
         */
        @Override
        public void removedPoblacion(ControllerPoblacionEvent event) {            
            boolean removed = false;
            //Recorremos el árbol buscando el experimento a borrar
            for(int i=0; i<arbolExperimentos.getRowCount() && !removed; i++) {
                TreePath pathExp = arbolExperimentos.getPathForRow(i);
                //Si el path es de dos niveles es un experimento
                //Primer nivel es root
                //Segundo nivel el path del experimento
                //Tercer nivel el path de poblacion
                if(pathExp.getPath().length == 3) {
                    LogicPoblacion po = (LogicPoblacion)
                            ((DefaultMutableTreeNode)pathExp.getPath()[2])
                            .getUserObject();
                    
                    
                    
                    if(po.equals(event.getPoblacion())) {
                        ((DefaultMutableTreeNode)pathExp.getPath()[1]).remove(
                            ((DefaultMutableTreeNode)pathExp.getPath()[2]));
                        modeloArbol.reload();
                        removed = true;
                    }
                }
            }
        }
        
        /**
         * Método que es llamado al modificarse una población, repintamos el
         * JTree por si su nombre ha cambiado
         * @param event 
         */
        @Override
        public void modifiedPoblacion(ControllerPoblacionEvent event) {
            arbolExperimentos.repaint();
        }
    }
    
    /**
     * Método que es llamado cuando el idioma cambia
     * @param evt LanguageEvent
     */
    @Override
    public void idiomaChange(LanguageEvent evt) {
        padreArbol.setUserObject(Language.getI().getP("EXPERIMENTOS"));
        arbolExperimentos.repaint();
    }
}
